using System.Linq.Expressions;

namespace Project_Schedule.Domain.BackGround.com.core.utils;

public static class PagingQuery
{
    public static IQueryable<T> TakePage<T>(this IQueryable<T> queryable, int page, int size = 15)
    {
        return queryable.TakeOrderByPage<T>(page, size);
    }

    public static IQueryable<T> TakeOrderByPage<T>(this IQueryable<T> queryable, int page, int size = 15,
        Expression<Func<T, Dictionary<object, QueryOrderBy>>>? orderBy = null)
    {
        if (page <= 0)
        {
            page = 1;
        }

        return queryable.GetIQueryableOrderBy(orderBy.GetExpressionToDic())
            .Skip((page - 1) * size)
            .Take(size);
    }

    public static IQueryable<TEntity> GetIQueryableOrderBy<TEntity>(this IQueryable<TEntity> queryable,
        Dictionary<string, QueryOrderBy> orderBySelector)
    {
        string[] orderByKeys = orderBySelector.Select(x => x.Key).ToArray();
        if (orderByKeys == null || orderByKeys.Length == 0) return queryable;

        IOrderedQueryable<TEntity> queryableOrderBy;

        var orderByKey = orderByKeys[^1];
        queryableOrderBy = orderBySelector[orderByKey] == QueryOrderBy.Desc
            ? queryableOrderBy = queryable.OrderByDescending(orderByKey.GetExpression<TEntity>())
            : queryable.OrderBy(orderByKey.GetExpression<TEntity>());

        for (var i = orderByKeys.Length - 2; i >= 0; i--)
        {
            queryableOrderBy = orderBySelector[orderByKeys[i]] == QueryOrderBy.Desc
                ? queryableOrderBy.ThenByDescending(orderByKeys[i].GetExpression<TEntity>())
                : queryableOrderBy.ThenBy(orderByKeys[i].GetExpression<TEntity>());
        }

        return queryableOrderBy;
    }

    public static Expression<Func<T, object>> GetExpression<T>(this string propertyName)
    {
        return propertyName.GetExpression<T, object>(typeof(T).GetExpressionParameter());
    }

    public static ParameterExpression GetExpressionParameter(this Type type)
    {
        return Expression.Parameter(type, "p");
    }

    public static Expression<Func<T, TKey>> GetExpression<T, TKey>(this string propertyName,
        ParameterExpression parameter)
    {
        if (typeof(TKey).Name == "Object")
            return Expression.Lambda<Func<T, TKey>>(
                Expression.Convert(Expression.Property(parameter, propertyName), typeof(object)), parameter);
        return Expression.Lambda<Func<T, TKey>>(Expression.Property(parameter, propertyName), parameter);
    }

    public static Dictionary<string, QueryOrderBy> GetExpressionToDic<T>(
        this Expression<Func<T, Dictionary<object, QueryOrderBy>>>? expression)
    {
        return expression == null
            ? new Dictionary<string, QueryOrderBy>()
            : expression.GetExpressionToPair()
                .Reverse()
                .ToList()
                .ToDictionary(
                    x =>
                        x.Key,
                    x =>
                        x.Value);
    }

    public static IEnumerable<KeyValuePair<string, QueryOrderBy>> GetExpressionToPair<T>(
        this Expression<Func<T, Dictionary<object, QueryOrderBy>>> expression)
    {
        foreach (var exp in ((ListInitExpression)expression.Body).Initializers)
        {
            yield return new KeyValuePair<string, QueryOrderBy>(
                exp.Arguments[0] is MemberExpression
                    ? (exp.Arguments[0] as MemberExpression).Member.Name.ToString()
                    : ((exp.Arguments[0] as UnaryExpression).Operand as MemberExpression).Member.Name,
                (QueryOrderBy)(
                    exp.Arguments[1] as ConstantExpression != null
                        ? (exp.Arguments[1] as ConstantExpression).Value
                        //2021.07.04增加自定排序按条件表达式
                        : Expression.Lambda<Func<QueryOrderBy>>(exp.Arguments[1] as Expression).Compile()()
                ));
        }
    }
}